﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;

namespace MapSurface.UIModelElement
{
    public class UIModelManager
    {
        #region Properties
        private UIModel _model;

        private UpdateUIModel _xmiUpdate;

        private XmlDocument _docXML;



        #endregion

        #region Constructors
        public UIModel Model
        {
            get { return _model; }
            set { _model = value; }
        }
        public UIModelManager(String UIModelfile, String dllFile)
        {
            _docXML = new XmlDocument();
            _docXML.Load(UIModelfile);
            _model = build(_docXML.DocumentElement, UIModelfile);
            _xmiUpdate = new UpdateUIModel(UIModelfile);
        }

        public void doLoadModel()
        {
            EventHandler handler = UIModelLoadHappened;
            if (handler != null)
                handler(_model, null);
        }
        #endregion

        #region XMLConstructors
        public UIModel build(XmlElement root, string xmiFile)
        {
            if (root.Name.Contains("UIModel"))
            {
                UIModel model = new UIModel();
                model.XmiFile = xmiFile;
                model.Node = root;
                model.Name = root.Attributes.GetNamedItem("name").Value;
                if (root.HasChildNodes)
                {
                    model.Contains = new List<UIElement>();
                    foreach (XmlNode node in root.ChildNodes)
                    {
                        model.Contains.Add((UIElement)build(node));
                    }
                }
                return model;
            }
            return null;
        }
        public UIElement build(XmlNode node)
        {
            UIElement cont = null;
            if (isContainer(node))
                cont = new Container(node);
            else 
                cont = new UIElement(node);
            return cont;
        }
        private void visitChild(XmlNode node, UIElement cont)
        {
            foreach (XmlNode child in node.ChildNodes)
                if (isProperty(child))
                {
                    Property p = new Property(getPropertyValue(child, "name"), getPropertyValue(child, "value"), getPropertyValue(child, "valueType"));
                    p.Node = child;
                    cont.Properties.Add(p);
                }
                else if (isWidget(child))
                {
                    Widget w = new Widget(child);
                    w.Name = child.Attributes.GetNamedItem("name").Value;
                    w.Node = child;
                    cont.WidgetType = w;
                }
                else if (isEvent(child))
                {
                    Event e = new Event(child.Attributes.GetNamedItem("name").Value, "", null);
                    e.Node = child;
                    if (cont.Events == null)
                        cont.Events = new List<Event>();
                    cont.Events.Add(e);

                }
        }
        private void visitChild(XmlNode node, Container cont)
        {
            foreach (XmlNode child in node.ChildNodes)
                if (isProperty(child))
                {
                    Property p = new Property(getPropertyValue(child, "name"), getPropertyValue(child, "value"), getPropertyValue(child, "valueType"));
                    p.Node = child;
                    cont.Properties.Add(p);
                }
                else if (isWidget(child))
                {
                    Widget w = new Widget(child);
                    w.Name = child.Attributes.GetNamedItem("name").Value;
                    w.Node = child;
                    cont.WidgetType = w;
                    foreach (XmlNode wc in child.ChildNodes)
                    {
                        if (isContainsWidget(wc))
                        {
                            Widget cw = new Widget(wc);
                            cw.Name = wc.Attributes.GetNamedItem("name").Value;
                            if (w.ContainsWidgets == null)
                                w.ContainsWidgets = cw;
                            break;
                        }
                    }
                }
                else if (isEvent(child))
                {
                    Event e = new Event(child.Attributes.GetNamedItem("name").Value, "", null);
                    e.Node = child;
                    if (cont.Events == null)
                        cont.Events = new List<Event>();
                    cont.Events.Add(e);
                }
                else if (isContainer(child) || isUIElement(child))
                {
                    if (cont.Contains == null)
                        cont.Contains = new List<UIElement>();
                    cont.Contains.Add((UIElement)build(child));
                }
        }
        public bool isContainer(XmlNode node)
        {
            foreach (XmlAttribute att in node.Attributes)
                if (att.Name.CompareTo("xsi:type") == 0)
                    if (att.Value.CompareTo("UIModel:Container") == 0)
                        return true;
            return false;
        }
        public bool isUIElement(XmlNode node)
        {
            if (node.Name.Contains("UIElement"))
                return true;
            return false;
        }
        public bool isProperty(XmlNode node)
        {
            if (node.Name.Contains("properties"))
                return true;
            return false;
        }
        public bool isEvent(XmlNode node)
        {
            if (node.Name.Contains("events"))
                return true;
            return false;
        }
        public bool isWidget(XmlNode node)
        {
            if (node.Name.Contains("widgetType"))
                return true;
            else
                return false;
        }
        public bool isContainsWidget(XmlNode node)
        {
            if (node.Name.Contains("containsWidget"))
                return true;
            return false;
        }
        #endregion

        #region Finders
        public String getPropertyValue(XmlNode node, String name)
        {
            foreach (XmlNode att in node.Attributes)
                if (att.Name.CompareTo(name) == 0)
                {
                    return att.Value;
                }
            return "";
        }
        public UIElement findElement(int id)
        {
            foreach (UIElement el in _model.Contains)
                if (el.Id == id)
                    return el;
                else
                    try
                    {
                        return findElement((Container)el, id);
                    }
                    catch
                    {

                    }
            return null;
        }
        public UIElement findElement(Container model, int id)
        {
            foreach (UIElement el in model.Contains)
                if (el.Id == id)
                    return el;
                else
                    try
                    {
                        return findElement((Container)el, id);
                    }
                    catch
                    {

                    }
            return null;
        }
        public UIElement findInstance(object instance)
        {
            String t = ((System.Windows.FrameworkElement)instance).Name;
            foreach (UIElement el in _model.Contains)
                if (el.Name.CompareTo(t)==0)
                    return el;
                else
                    try
                    {
                        return findInstance((Container)el, instance);
                    }
                    catch
                    {

                    }
            return null;

        }
        public UIElement findInstance(UIElement container, object instance)
        {
            if (container.XalmObject.GetHashCode() == instance.GetHashCode())
                return container;
            else
                try
                {
                    foreach (UIElement el in ((Container)container).Contains)
                    {
                        UIElement uie = findInstance(el, instance);
                        if (uie != null)
                            return uie;
                        else findInstance(el, instance);
                    }
                }
                catch
                {

                }
            return null;

        }

        public Container findDadElement(Container dad, int id)
        {
            foreach (UIElement el in dad.Contains)
                if (el.Id == id)
                    return dad;
                else
                    try
                    {
                        return findDadElement((Container)el, id);
                    }
                    catch
                    {

                    }
            return null;
        }

        public void findWidgetElement(Container dad,string widget, ref List<UIElement> liste)
        {
            if (liste == null)
                liste = new List<UIElement>();
            if (dad.WidgetType.Name.CompareTo(widget) == 0)
                liste.Add(dad);
            foreach (UIElement el in dad.Contains)
                    try
                    {
                        findWidgetElement((Container)el, widget, ref liste);
                    }
                    catch
                    {
                        if (el.WidgetType.Name.CompareTo(widget) == 0)
                            liste.Add(el);
                    }
        }

        #endregion

        #region Operations
        public bool removeElement(UIElement element)
        {
            if (element != null)
            {
                XmlNode dad = element.XamlNode.ParentNode;
                if (dad != null)
                {
                    String Dadid = null;
                    if (dad.Attributes.GetNamedItem("id") != null)
                        Dadid = dad.Attributes.GetNamedItem("id").Value;
                    Container cont = null;
                    if ( Dadid != null)
                        cont = (Container)findElement(int.Parse(Dadid));
                    else
                        cont = (Container)_model.Contains[0];
                    EventHandler remove = UIModelChecker;
                    if(remove!=null )
                        remove(checkCoherence(element), null);
                    if(cont!=null)
                        cont.Contains.Remove(element);
                    _xmiUpdate.deleteUINode(element.Name);
                    return true;

                }

            }
            return false;
        }
        private string checkCoherence(UIElement node)
        {
            String Message = "";
            if (node.Events != null)
            {
                Message = node.Name+" has link with FC through  \n";
                foreach (Event e in node.Events)
                    if (e.ScriptCode != null)
                        Message += e.Name + "=" + e.ScriptCode + "\n";
                    else Message+=e.Name+"\n";
            }
            else
            {
                try
                {
                    if (((Container)node).Contains != null)
                        Message += node.Name + " contains other widgets \n";
                }
                catch (InvalidCastException)
                {
                }
            }
            Message += "Do you want to delete it ?";
            return Message;
        }
        public bool replaceWidgetType(object instance , UIElement newElement)
        {
            UIElement element = findInstance(instance);
            if (element != null)
            {
                replaceWidgetType(element, newElement);
                saveModel(_model.XmiFile);
            }

            return false;
        }

        private List<UIElement> getChild(UIElement element)
        {
            List<UIElement> child = new List<UIElement>();
            try
            {
                if (element.WidgetType.ContainsWidgets == null && ((Container)element).Contains!=null)
                    foreach (UIElement e in ((Container)element).Contains)
                        child.Add(e);
                else if ( ((Container)element).Contains != null)
                    foreach (UIElement e in ((Container)element).Contains)
                        if(((Container)e).Contains!=null)
                            child.Add(((Container)e).Contains[0]);
            }
            catch (InvalidCastException)
            {
                return null;
            }
            return child;
        }
        private Container getDad(Container container, UIElement element)
        {
            if(container.Contains!=null)
            foreach (UIElement e in container.Contains)
                if (e.Id == element.Id && e.Name.CompareTo(element.Name) == 0&& e.WidgetType.Name.CompareTo(element.WidgetType.Name)==0)
                    return container;
                else
                {
                    try
                    {
                        Container dad = getDad((Container)e, element);
                        if (dad != null)
                            return dad;
                    }
                    catch (InvalidCastException)
                    {
                    }
                    
                }
            return null;
        }

        public bool replaceWidgetType(UIElement oldElement, UIElement newElement)
        {
            Container dad = getDad((Container)_model.Contains[0], oldElement);
            List<UIElement> childs= getChild(oldElement);
            if(childs!=null)
            {
                try
                {
                    if (newElement.WidgetType.ContainsWidgets == null)
                    {
                        foreach (UIElement e in childs)
                        {
                            try
                            {
                                //if (((Container)oldElement).Contains.Remove(e))
                                if (((Container)newElement).Contains == null)
                                    ((Container)newElement).Contains = new List<UIElement>();
                                ((Container)newElement).Contains.Add(e);
                            }
                            catch (InvalidCastException)
                            {
                            }
                        }
                    }
                    else
                    {
                        int id=0;
                        UIElement c=null;
                        if (((Container)newElement).Contains.Count != 0)
                        {
                            c = ((Container)newElement).Contains[0];
                            ((Container)newElement).Contains.RemoveAt(0);
                            id = c.Id;
                            foreach (UIElement e in childs)
                            {
                                id++;
                                List<UIElement> ci = new List<UIElement>();
                                ci.Add(e);
                                Container cont = new Container(c.XamlNode, id, c.Name + "" + id, ci);
                                ((Container)newElement).Contains.Add(cont);
                            }
                        }
                        else
                            foreach (UIElement e in childs)
                                ((Container)newElement).Contains.Add(e);
                    }
                }
                catch(Exception e) {
                    Console.Out.WriteLine( e.Message);
                }
            }
            if (dad == null)
            {
                _model.Contains = new List<UIElement>();
                _model.Contains.Add(newElement);
            }
            else
                if (dad.Contains.Remove(oldElement))
                    dad.Contains.Add(newElement);

            return true;
        }

        public void replaceAllWidgetType(string widgetType, UIElement newInstance)
        {
            List<UIElement> list = null;
            findWidgetElement((Container)_model.Contains[0], widgetType, ref list);
            if (list != null)
                foreach (UIElement i in list)
                    replaceWidgetType(i, newInstance);
            saveModel(_model.XmiFile);
        }

        private void saveModel(string filenName)
        {
            string file = _model.toString();
            // Create the file.
            using (System.IO.FileStream fs = System.IO.File.Create(filenName, 1024))
            {
                // Add some information to the file.
                byte[] info = new System.Text.ASCIIEncoding().GetBytes(file);
                fs.Write(info, 0, info.Length);
            }
            XmlDocument docu = new XmlDocument();
            docu.Load(filenName);
            docu.Save(filenName);
        }


        #endregion

        #region Observers Handlers

        public void DeleteUIElementHandler(object sender, EventArgs args)
        {
            UIElement e = findInstance(sender);
            if(e!=null)
                removeElement(e);

        }

        public void ReplaceUIElement(object sender, EventArgs args)
        {
            object[] param =(object[]) sender;
            object instance = param[0];
            object newO = param[1];
  
            bool all = (bool)param[2];
            if (all)
                replaceAllWidgetType(instance.GetType().Name,(UIElement) newO);
            else if (newO != null)
                replaceWidgetType(instance, (UIElement)newO);
        }
        public void CheckUIModel(object sender, EventArgs e)
        {

            if (sender != null)
            {
                UIElement element = findInstance(sender);
                if (element != null)
                {
                    EventHandler checker = CheckBeforeDelete;
                    if (checker != null)
                        checker(checkCoherence(element), null);
                }
                else if(sender!=null)
                {
                    EventHandler checker = CheckBeforeDelete;
                    if (checker != null)
                        checker( sender.GetType().Name+" Can be deleted", null);

                }
            }
        }

        #endregion

        #region Observables UIModels Events

        public event EventHandler UIModelLoadHappened;
        public event EventHandler UIModelChecker;
        public event EventHandler CheckBeforeDelete;
        #endregion
    }
}
